home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Demos / A.D. Software / OOFILE / Buildable, limited OOFILE / source / reports / oofrw.cpp < prev    next >
Text File  |  1996-02-13  |  14KB  |  541 lines

  1. // COPYRIGHT 1994 A.D. Software, All rights reserved
  2.  
  3. // report-writer layer of OOFILE database
  4.  
  5. // NOTE inline definitions included at end of this header file
  6.  
  7. #include "oof3.hpp"  // knows a bit about fields
  8. #include "oofrw.hpp"
  9. #include <string.h>
  10.  
  11. unsigned int dbRepLine::mWidth;
  12.  
  13. // -------------------------------------------------------
  14. //             d b R e p O v e r R u n
  15. // -------------------------------------------------------
  16.  
  17. // -------------------------------------------------------
  18. //                d b R e p L i n e
  19. // -------------------------------------------------------
  20.  
  21. dbRepLine::~dbRepLine()
  22. {
  23.     delete[] mLine;
  24. }
  25.  
  26.  
  27. void 
  28. dbRepLine::prepare()
  29. // Generate a new Line and fill it with whitespace
  30. //
  31. {
  32.     if(!mLine){
  33.         mLine = new char[mWidth+1];        // We'll null-terminate just to be safe
  34.         assert(mLine);
  35.         mLine[mWidth]='\0';
  36.         memset(mLine, 0x20, mWidth);    // 0x20 is ASCII for space
  37. #ifdef OOF_SmartHeap
  38.     assert(MemPoolCheck(MemDefaultPool));
  39. #endif
  40.     }
  41. }
  42.  
  43.  
  44. void 
  45. dbRepLine::clear()
  46. {
  47.     delete[] mLine;
  48.     mLine = 0;
  49. }
  50.  
  51. void dbRepLine::drawNCharsAt(unsigned int hPos, char* theChars, unsigned int len)
  52. {
  53.     prepare();                // Make sure we have some space to draw on
  54.     unsigned long memLen=strlen(theChars);
  55.     if(memLen>len)
  56.         memLen=len;
  57.     memcpy(&mLine[hPos],theChars,memLen);
  58. #ifdef OOF_SmartHeap
  59.     assert(MemPoolCheck(MemDefaultPool));
  60. #endif
  61. }
  62.  
  63.  
  64. void 
  65. dbRepLine::fillNCharsAt(unsigned int hPos, char theChar, unsigned int len)
  66. {
  67.     prepare();                // Make sure we have some space to draw on
  68.     memset(&mLine[hPos],theChar,len);
  69. #ifdef OOF_SmartHeap
  70.     assert(MemPoolCheck(MemDefaultPool));
  71. #endif
  72. }
  73.  
  74.  
  75. void 
  76. dbRepLine::drawToStream(unsigned int hPos, unsigned int len, ostream& os)
  77. {
  78.     if(mLine)
  79.         os.write(&mLine[hPos],len);
  80. }
  81.  
  82. // -------------------------------------------------------
  83. //               d b R e p S i z e r
  84. // -------------------------------------------------------
  85.  
  86.  
  87. // -------------------------------------------------------
  88. //                d b R e p P a g e
  89. // -------------------------------------------------------
  90.  
  91. dbRepPage::dbRepPage() : 
  92.                                         mPageMap(0),
  93.                                         mFieldPos(0),
  94.                                         mPageStart(0)
  95. {
  96. // Do nothing - We are built by the appropriate FormatFor... routine.
  97. //
  98. //   At present, only CharStream is supported
  99. };
  100.  
  101.  
  102. dbRepPage::~dbRepPage()
  103. {
  104.     delete[] mPageMap;
  105. }
  106.  
  107.  
  108. void 
  109. dbRepPage::draw(dbRepSizer Sizer, ostream& os)
  110. {
  111.     for(unsigned int pageNum=0;pageNum<mNumPages;pageNum++)
  112.     {
  113.         for(unsigned int i=0;i<Sizer.mTopMargin;i++)          // Top Margin
  114.             os << endl;
  115.         
  116.         unsigned int thispageWidth;                                                // Work out how much we have to draw
  117.         if (pageNum+1==mNumPages) {
  118.             thispageWidth=mWidth-mPageBreak[pageNum];
  119.         } else {
  120.             thispageWidth=mPageBreak[pageNum+1]-mPageBreak[pageNum];
  121.         }
  122.         for(unsigned int lineNum=0;lineNum<mNumLines;lineNum++)
  123.         {
  124.             for(unsigned int l=0;l<Sizer.mLeftMargin;l++)
  125.                 os << ' ';
  126.             mPageMap[lineNum].drawToStream(mPageBreak[pageNum],thispageWidth,os);    // draw the stuff
  127.             os << endl;
  128.         }
  129.         for(unsigned int k=0;k<Sizer.mBottomMargin;k++)        // Bottom Margin
  130.             os << endl;
  131.         
  132.         endPage(os);
  133.     }
  134. }
  135.  
  136.  
  137. void 
  138. dbRepPage::clearLines(unsigned int start, unsigned int end)
  139. {
  140.     assert(end>=start);
  141.     assert(start<mNumLines);
  142.     
  143.     for(unsigned int i=start;i<=end;i++)
  144.         mPageMap[i].clear();
  145. }
  146.  
  147.  
  148. void 
  149. dbRepPage::endPage(ostream& os)
  150. {
  151.     os << "---cut here---" << endl;
  152. }
  153.  
  154. // -------------------------------------------------------
  155. //                      d b R e p
  156. // -------------------------------------------------------
  157.  
  158. void 
  159. dbRep::extract(ostream& os)
  160. {
  161.   unsigned int rNum=1;
  162.  
  163.     unsigned int numFields = mFields.count();
  164.     mFields.source()->start();        // start record iteration (vertical)
  165.     while (mFields.source()->more()) {
  166.         os << endl << "Record# " << rNum++ << endl;
  167.         for (unsigned int i=0; i<numFields; i++) {        // start field iteration (horizontal)
  168.             dbField  *theField = (dbField  *) mFields[i];           // safe downcast
  169.             char *theString = theField->copyAsChars();
  170.             os << theField->fieldName() << " : " << theString << endl;
  171.             delete theString;
  172.         }
  173.     
  174.         mFields.source()->next();    
  175.     }
  176. }
  177.  
  178. char *dbRep::copyStr(char *theString)
  179. {
  180.     unsigned long dataLen = strlen(theString);
  181.  
  182.     char *retStr = new char[dataLen+1];
  183.     
  184.     strcpy(retStr,theString);
  185.     
  186.     return(retStr);
  187. }
  188.  
  189.  
  190. void 
  191. dbRep::setStyle(const ReportStyles style)
  192. {
  193.     mReportStyle = style;
  194. }
  195.  
  196. // -------------------------------------------------------
  197. //                  d b R e p C h a r
  198. // -------------------------------------------------------
  199.  
  200.  
  201. void 
  202. dbRepChar::formatForCharStream()
  203. // Builds the contents of our dbRepPage for Character Stream.
  204. //
  205. {
  206.     unsigned int numfields;
  207.     
  208.     switch(mReportStyle) {
  209.         case columnar: numfields = mFields.count();
  210.                                         break;
  211.                                         
  212.         case pageWise: numfields = 2;
  213.                                         break;
  214.         
  215.         default: assert(false);
  216.     }
  217.     
  218.     mPage.mFieldPos = new unsigned int[numfields];
  219.  
  220.     unsigned int Printable = mSizer.mPageWidth - (mSizer.mLeftMargin + mSizer.mRightMargin);        // Actual Printable Width
  221.     
  222.     mPage.mWidth = 0;
  223.     int FirstOnPage = true;
  224.     unsigned int CurrpageWidth = 0;
  225.     mPage.mNumPages = 1;
  226.     mPage.mPageBreak.append(0);
  227.     for (unsigned int i=0; i<numfields; i++)
  228.     {
  229.         mPage.mFieldPos[i] = mPage.mWidth;
  230.         if (CurrpageWidth + mColWidths[i] <= Printable) {            // Can we fit the next field in this page ?
  231.             mPage.mWidth+=mColWidths[i];
  232.             CurrpageWidth+=mColWidths[i];
  233.             FirstOnPage = false;
  234.             if (CurrpageWidth + mSizer.mColSepWidth <= Printable) {        // Add space between columns
  235.                 mPage.mWidth+=mSizer.mColSepWidth;
  236.                 CurrpageWidth+=mSizer.mColSepWidth;
  237.             } else {
  238.                 if(i!=numfields-1) {                                                                // If there are any more fields...
  239.                     mPage.mNumPages++;                                                                // Time for a new page
  240.                     if(mReportStyle==pageWise)
  241.                         assert(false);
  242.                     mPage.mPageBreak.append(mPage.mWidth);
  243.                     FirstOnPage = true;
  244.                     CurrpageWidth = 0;
  245.                 }
  246.             }
  247.         } else {
  248.             if (FirstOnPage) {
  249.                 mColWidths[i] = Printable;                                                // We're the only field on this page - We'll have to squeeze in
  250.                 CurrpageWidth+=Printable;
  251.                 mPage.mWidth+=Printable;
  252.                 FirstOnPage = false;
  253.             } else {
  254.                 i--;                                                                                            // Go back and fetch this one again on a new page
  255.                 mPage.mNumPages++;                                                                // Time for a new page
  256.                 mPage.mPageBreak.append(mPage.mWidth);
  257.                 FirstOnPage = true;
  258.                 CurrpageWidth = 0;
  259.             }
  260.         }
  261.     }
  262.  
  263.     mPage.mNumLines = mSizer.mPageHeight - (mSizer.mTopMargin + mSizer.mBottomMargin);                        // Actual Printable Height
  264.     assert(mPage.mNumLines>0);
  265.     mPage.mPageMap = new dbRepLine[mPage.mNumLines];
  266.     
  267.     dbRepLine::mWidth = mPage.mWidth;
  268.     mBuilt = true;
  269. }
  270.  
  271.  
  272. void 
  273. dbRepChar::drawHeader(ostream&)
  274. {
  275.     switch(mReportStyle) {
  276.         case columnar: {    unsigned int numFields = mFields.count();
  277.                                 for(unsigned int i=0;i<numFields;i++)
  278.                                         {
  279.                                             dbField  *theField = (dbField  *) mFields[i];  // safe downcast (Or so the Guru tells me !)
  280.                                             mPage.mPageMap[0].drawNCharsAt(mPage.mFieldPos[i],theField->fieldName(),mColWidths[i]);
  281.                                             mPage.mPageMap[1].fillNCharsAt(mPage.mFieldPos[i],'-',mColWidths[i]);
  282.                                         }
  283.                                         mPage.mBodyStart = 3;
  284.                                         break;
  285.                                         }
  286.                                         
  287.         case pageWise: mPage.mBodyStart = 0;            // PageWise has no header.]
  288.                                       break;
  289.         
  290.         default:assert(false);
  291.     }
  292. }
  293.  
  294.  
  295. unsigned int 
  296. dbRepChar::drawWrappedChars(unsigned int line,unsigned int hPos,unsigned int width,char **theString)
  297. {
  298.         unsigned long dataLen = strlen(*theString);
  299.         unsigned int numLines = 0;
  300.         
  301.         while((dataLen>0)&&(line+numLines<mPage.mNumLines)) {
  302.         
  303.             unsigned int i=0;
  304.             
  305.             char *simpleStr=*theString;
  306.             
  307.             for(;((simpleStr[i]!='\0')&&(simpleStr[i]!='\n')&&(i<width));i++)
  308.                 ;
  309.                     // Hunt down and kill carriage returns
  310.                     // Perhaps we should convert tabs to spaces as well ?
  311.                     
  312.             if (i==width) {                        // we ran out of room - back up to the end of the last word.
  313.                 for(;((simpleStr[i]!=' ')&&(i>0));i--)
  314.                     ;
  315.                 if(i==0)
  316.                     i=width;                            // the word was too long for the field width - so we'll just chop it !
  317.             }
  318.             
  319.             *theString+=i;
  320.             dataLen-=i;
  321.             if ((simpleStr[i]=='\n')||(simpleStr[i]==' '))    {
  322.                 *theString+=1;                    // skip over the CR or the space
  323.                 dataLen--;
  324.             }
  325.  
  326.             mPage.mPageMap[line+numLines].drawNCharsAt(hPos,simpleStr,i);                // draw the stuff !
  327.             
  328.             numLines++;
  329.         }
  330.  
  331.         if (dataLen>0)        // It won't fit !
  332.             numLines=0xff;        
  333.         
  334.         return(numLines);
  335. }
  336.  
  337.  
  338. void 
  339. dbRepChar::drawColumnar(ostream& os)
  340. {
  341. // NOTE: mFields.table = the database/selection
  342. // mFields = ordered list of fields to report
  343.  
  344.     int FirstOnPage = true;
  345.     
  346.   dbRepOverRun *recOverRun = new dbRepOverRun[mFields.count()];
  347.     
  348.     unsigned int currentLine = mPage.mBodyStart;
  349.     char *theString;
  350.     char *deleteMe;
  351.     int IsOverRun = false;
  352.     int OverRunning = false;
  353.  
  354.     unsigned int numFields = mFields.count();
  355.     mFields.source()->start();        // start record iteration (vertical)
  356.     while (mFields.source()->more()) {
  357.  
  358.         int Advance = true;
  359.         unsigned int maxLines = 0;
  360.  
  361.         for (unsigned int i=0; i<numFields; i++) {        // start field iteration (horizontal)
  362.             dbField  *theField = (dbField  *) mFields[i];           // safe downcast
  363.             
  364.             if(IsOverRun)
  365.             {
  366.                 if(!recOverRun[i].OverRun)
  367.                     continue;
  368.                 theString = recOverRun[i].OverRun;
  369.                 deleteMe = recOverRun[i].DeleteMe;
  370.                 recOverRun[i].OverRun=0;
  371.             } else {
  372.                 theString = theField->copyAsChars();                    // retrieve the field data as a char*
  373.                 deleteMe = theString;                                                    // keep track of our original pointer
  374.             }
  375.             
  376.             unsigned int linesUsed = drawWrappedChars(currentLine,mPage.mFieldPos[i],mColWidths[i],&theString);
  377.             // WARNING - drawWrappedChars WILL change the value of theString !
  378.             
  379.             if (linesUsed==0xff)
  380.                 if (FirstOnPage) {
  381.                     recOverRun[i].OverRun=theString;
  382.                     recOverRun[i].DeleteMe=deleteMe;
  383.                     Advance = false;
  384.                     OverRunning = true;
  385.                     linesUsed = mPage.mNumLines-currentLine;
  386.                 } else {
  387.                     mPage.clearLines(currentLine,mPage.mNumLines-1);
  388.                     currentLine = mPage.mNumLines;
  389.                     delete[] deleteMe;
  390.                     Advance = false;
  391.                     break;
  392.                 }
  393.             
  394.             if (linesUsed>maxLines)
  395.                 maxLines=linesUsed;
  396.             
  397.             if((!OverRunning)||(OverRunning&&(recOverRun[i].DeleteMe!=deleteMe)))
  398.                 delete[] deleteMe;                                                                    // throw it away now that we're done
  399.         }
  400.         IsOverRun = OverRunning;
  401.         OverRunning = false;
  402.  
  403.         currentLine+=(mSizer.mBlockVertSep + maxLines);
  404.         
  405.         FirstOnPage = false;
  406.         
  407.         if(currentLine>=mPage.mNumLines) {                                    // time to start a new page
  408.             mPage.draw(mSizer,os);
  409.             mPage.clearLines(mPage.mBodyStart,mPage.mNumLines-1);
  410.             currentLine = mPage.mBodyStart;
  411.             FirstOnPage = true;
  412.         }
  413.         if (Advance)
  414.             mFields.source()->next();
  415.     }
  416.     
  417.     if(currentLine != mPage.mBodyStart)
  418.         mPage.draw(mSizer, os);
  419. }
  420.  
  421.  
  422. void 
  423. dbRepChar::drawPageWise(ostream& os)
  424. {
  425. // NOTE: mFields.table = the database/selection
  426. // mFields = ordered list of fields to report
  427.  
  428.     int FirstOnPage = true;
  429.     
  430.     unsigned int currentLine = mPage.mBodyStart;
  431.     char *theString;
  432.     char *deleteMe;
  433.     unsigned int fNum;
  434.  
  435.   dbRepOverRun *recOverRun = new dbRepOverRun[2];    
  436.     int IsOverRun = false;
  437.     int OverRunning = false;
  438.  
  439.    unsigned int numFields = mFields.count();
  440.     mFields.source()->start();        // start record iteration (vertical)
  441.     while (mFields.source()->more()) {
  442.     
  443.         unsigned int recordStart = currentLine;
  444.         int Advance = true;
  445.         if (!IsOverRun)
  446.             fNum=0;                                                                                                            // start field iteration (horizontal)
  447.         
  448.         while (fNum<numFields) {
  449.             dbField  *theField = (dbField  *) mFields[fNum];                       // safe downcast
  450.             
  451.             unsigned int maxLines = 0;
  452.  
  453.             for (unsigned int i=0; i<2; i++) {                                                // page left->right
  454.                 if(IsOverRun)
  455.                 {
  456.                     if(!recOverRun[i].OverRun)
  457.                         continue;
  458.                     theString = recOverRun[i].OverRun;
  459.                     deleteMe = recOverRun[i].DeleteMe;
  460.                     recOverRun->OverRun=0;
  461.                 } else {
  462.                     if (i==0)
  463.                         theString = copyStr(theField->fieldName());    // retrieve the field name as a separate char*
  464.                     else
  465.                         theString = theField->copyAsChars();                // retrieve the field data as a char*
  466.                     deleteMe = theString;                                                    // keep track of our original pointer
  467.                 }
  468.                 
  469.                 unsigned int linesUsed = drawWrappedChars(currentLine,mPage.mFieldPos[i],mColWidths[i],&theString);
  470.                 // WARNING - drawWrappedChars WILL change the value of theString !
  471.                 
  472.                 if (linesUsed==0xff)
  473.                     if (FirstOnPage) {
  474.                         recOverRun[i].OverRun=theString;
  475.                         recOverRun[i].DeleteMe=deleteMe;
  476.                         Advance = false;
  477.                         OverRunning = true;
  478.                         currentLine = mPage.mNumLines;
  479.                     } else {
  480.                         mPage.clearLines(recordStart,mPage.mNumLines-1);
  481.                         currentLine = mPage.mNumLines;
  482.                         delete[] deleteMe;
  483.                         Advance = false;
  484.                         break;
  485.                     }
  486.                 
  487.                 if (linesUsed>maxLines)
  488.                     maxLines=linesUsed;
  489.                 
  490.                 if((!OverRunning)||(OverRunning&&(recOverRun[i].DeleteMe!=deleteMe)))
  491.                     delete[] deleteMe;                                                                    // throw it away now that we're done
  492.  
  493.             }
  494.             IsOverRun = OverRunning;
  495.             OverRunning = false;
  496.  
  497.             if(IsOverRun)
  498.                 break;
  499.                 
  500.             currentLine+=(mSizer.mBlockVertSep + maxLines);
  501.             
  502.             fNum++;                                                                                // Go on to the next field - We won't get here if we're OverRun
  503.         }
  504.         
  505.         FirstOnPage = false;
  506.         
  507.         if(currentLine>=mPage.mNumLines) {                                    // time to start a new page
  508.             mPage.draw(mSizer,os);
  509.             mPage.clearLines(mPage.mBodyStart,mPage.mNumLines-1);
  510.             currentLine = mPage.mBodyStart;
  511.             FirstOnPage = true;
  512.         }
  513.         if (Advance)
  514.             mFields.source()->next();
  515.     }
  516.     
  517.     if(currentLine != mPage.mBodyStart)
  518.         mPage.draw(mSizer, os);
  519. }
  520.  
  521.  
  522. void 
  523. dbRepChar::draw(ostream& os)
  524. {
  525.     if(!mBuilt)
  526.         formatForCharStream();
  527.         
  528.     drawHeader(os);
  529.  
  530.     switch(mReportStyle)  {
  531.         case columnar: drawColumnar(os);
  532.                                         break;
  533.         
  534.         case pageWise: drawPageWise(os);
  535.                                         break;
  536.         
  537.         default:assert(false);
  538.                          break;
  539.     }
  540. }
  541.